package com.example.chml7;

import android.util.Log;

public class Solver {
	private int N;
	private int K;
	private int M;
	private Functions data;
	private double h1;
	private double h2;
	
	private int cx;
	private int cy;
	
	public double eps = 0.0001;
	
	public char constVal = 'x';
	
	static final int exp_1 = 0;
	static final int exp_2_1 = 1;
	static final int exp_3_2 = 2;
	static final int exp_2_2 = 3;
	
	static final int imp_1 = 4;
	static final int imp_2_1 = 5;
	static final int imp_2_2 = 6;
	static final int imp_3_2 = 7;
	static final int real = 8;
	
	public Solver(int _N, int _M)
	{
		N = _N; M = _M;
	}
	
	public double getH1()
	{
		return h1;
	}
	
	public double getH2()
	{
		return h2;
	}
	
	public void setN(int _N)
	{
		N = _N;
		M = N;
	}
	
	public void setM(int _M)
	{
		M = _M;
		M = N;
	}
	
	public void setK(int _K)
	{
		K = _K;
	}
	
	public void setConstVal(char v)
	{
		if(v == 'x')
			constVal = 'x';
		else
			constVal = 'y';
	}
	
	public void setCX(double _cx)
	{
		cx = (int)(_cx/h1);
	}
	
	public void setCY(double _cy)
	{
		cy = (int)(_cy/h2);
	}
	
	public int getN()
	{
		return N;
	}
	
	public int getM()
	{
		return M;
	}
	
	public int getK()
	{
		return K;
	}
	
	public double getX0()
	{
		return data.x0;
	}
	
	public double getY0()
	{
		return data.y0;
	}
	
	public double getLx()
	{
		return data.Lx;
	}
	
	public double getLy()
	{
		return data.Ly;
	}
	
	public void setData(Functions _data)
	{
		data = _data;
		
        h1 = data.Lx/N;
        h2 = data.Ly/M;
	}
	
	public double[][] zedel()
	{
        double[][] u = new double[N][M];
        double[][] newU = new double[N][M];

        h1 = data.Lx/N;
        h2 = data.Ly/M;

        double sum = 0;
        for (int j1 = 1; j1 < N - 1; j1++)
            sum += data.phi_1(h2 * j1) + data.phi_2(h2 * j1) + data.phi_3(h1 * j1) + data.phi_4(h1 * j1);

        for (int i = 1; i < N - 1; i++)
            for (int j = 1; j < M - 1; j++)
                u[i][j] = sum / 4 / (N - 1);


        for (int j = 0; j < M; j++)
        {
            u[0][j] = (data.phi_1(h2 * j) - (data.Alpha1 / h1) * u[1][j]) / (-data.Alpha1 / h1 + data.Beta1);
            u[N - 1][j] = (data.phi_2(h2 * j) + (data.Alpha2 / h1) * u[N - 2][j]) / (data.Alpha2 / h1 + data.Beta2);
            u[j][0] = (data.phi_3(h1 * j) - (data.Alpha3 / h2) * u[j][1]) / (-data.Alpha3 / h2 + data.Beta3);
            u[j][N - 1] = (data.phi_4(h1 * j) + (data.Alpha4 / h2) * u[j][N - 2]) / (data.Alpha4 / h2 + data.Beta4);
        }
        double max = 0, oldMax;
        int k = 0;
        //for (int iter = 0; iter < eps; iter++ )
        do
        {
            oldMax = max;
            for (int i = 1; i < N - 1; i++)
                for (int j = 1; j < M - 1; j++)
                {
                	//newU[i][j] = (u[i + 1][j] + u[i - 1][j] + u[i][j + 1] + u[i][j - 1])/4;
                    //newU[i][j] = (u[i + 1][j] + newU[i - 1][j] + newU[i][j + 1] + u[i][j - 1]) / 4;
                	newU[i][j] = (u[i+1][j]*(1/(h1*h1) - data.A/h1) +
                			newU[i-1][j]*(1/(h1*h1)) + newU[i][j+1]*(1/(h2*h2)-data.B/h2) + 
                			u[i][j-1]*(1/(h2*h2)))/-(-2/(h1*h1) - 2/(h2*h2) + data.A/h1 + data.B/h2 - data.C);
                }

            for (int j = 0; j < M; j++)
            {
                newU[0][j] = (data.phi_1(h2 * j) - (data.Alpha1 / h1) * newU[1][j]) / (-data.Alpha1 / h1 + data.Beta1);
                newU[N - 1][j] = (data.phi_2(h2 * j) + (data.Alpha2 / h1) * newU[N - 2][j]) / (data.Alpha2 / h1 + data.Beta2);
                newU[j][0] = (data.phi_3(h1 * j) - (data.Alpha3 / h2) * newU[j][1]) / (-data.Alpha3 / h2 + data.Alpha3);
                newU[j][N - 1] = (data.phi_4(h1 * j) + (data.Alpha4 / h2) * newU[j][M - 2]) / (data.Alpha4 / h2 + data.Beta4);
            }


            for (int i = 0; i < N; i++)
                for (int j = 0; j < M; j++)
                    u[i][j] = newU[i][j];

            //u[0, 0] = u[0, n - 1] = u[n - 1, 0] = u[n - 1, n - 1] = 1000;

            max = 0;
            if (constVal == 'y')
            {
                for (int i = 0; i < N; i++)
                    if (Math.abs(u[cx][i] - data.uReal(cx * h1, h2 * i)) > max)
                        max = Math.abs(u[cx][i] - data.uReal(cx * h1, h2 * i));
            }

            if (constVal == 'x')
            {
                for (int i = 0; i < N; i++)
                    if (Math.abs(u[i][cy] - data.uReal(h1 * i, cy * h2)) > max)
                        max = Math.abs(u[i][cy] - data.uReal(cy * h1, h2 * i));
            }
            k++;
        } while (Math.abs(max - oldMax) > eps);
        return u;
	}
	
    public double[][] mpi()
    {
        double w=1;
        double[][] u = new double[N][M];
        double[][] newU = new double[N][M];


        double sum = 0;
        for (int j1 = 1; j1 < N - 1; j1++)
            sum = sum + data.phi_1(h2 * j1) + data.phi_2(h2 * j1) + data.phi_3(h1 * j1) + data.phi_4(h1 * j1);

        for (int i = 1; i < N - 1; i++)
            for (int j = 1; j < N - 1; j++)
                u[i][j] = sum / 4 / (N - 1);


        for (int j = 0; j < N; j++)
        {
            u[0][j] = (data.phi_1(h2 * j) - (data.Alpha1 / h1) * u[1][j]) / (-data.Alpha1 / h1 + data.Beta1);
            u[N - 1][j] = (data.phi_2(h2 * j) + (data.Alpha2 / h1) * u[N - 2][j]) / (data.Alpha2 / h1 + data.Beta2);
            u[j][0] = (data.phi_3(h1 * j) - (data.Alpha3 / h2) * u[j][1]) / (-data.Alpha3 / h2 + data.Beta3);
            u[j][N - 1] = (data.phi_4(h1 * j) + (data.Alpha4 / h2) * u[j][N - 2]) / (data.Alpha4 / h2 + data.Beta4);
        }
        double max = 0, oldMax;
        int k = 0;
        //for (int iter = 0; iter < eps; iter++ )
        do
        {
            oldMax = max;
            for (int i = 1; i < N - 1; i++)
                for (int j = 1; j < N - 1; j++)
                {
                    //newU[i][j] = u[i][j] +  w*((u[i + 1][j] + newU[i - 1][j] + newU[i][j + 1] + u[i][j - 1]) / 4 - u[i][j]);
                	newU[i][j] = u[i][j] + w*((u[i+1][j]*(1/(h1*h1) - data.A/h1) +
                			newU[i-1][j]*(1/(h1*h1)) + newU[i][j+1]*(1/(h2*h2)-data.B/h2) + 
                			u[i][j-1]*(1/(h2*h2)))/-(-2/(h1*h1) - 2/(h2*h2) + data.A/h1 + data.B/h2 - data.C) - u[i][j]);
                }

            for (int j = 0; j < N; j++)
            {
                newU[0][j] = (data.phi_1(h2 * j) - (data.Alpha1 / h1) * newU[1][j]) / (-data.Alpha1 / h1 + data.Beta1);
                newU[N - 1][j] = (data.phi_2(h2 * j) + (data.Alpha2 / h1) * newU[N - 2][j]) / (data.Alpha2 / h1 + data.Beta2);
                newU[j][0] = (data.phi_3(h1 * j) - (data.Alpha3 / h2) * newU[j][1]) / (-data.Alpha3 / h2 + data.Beta3);
                newU[j][N - 1] = (data.phi_4(h1 * j) + (data.Alpha4 / h2) * newU[j][N - 2]) / (data.Alpha4 / h2 + data.Beta4);
            }


            for (int i = 0; i < N; i++)
                for (int j = 0; j < N; j++)
                    u[i][j] = newU[i][j];

            //u[0, 0] = u[0, n - 1] = u[n - 1, 0] = u[n - 1, n - 1] = 1000;

            max = 0;
            if (constVal == 'y')
            {
                for (int i = 0; i < N; i++)
                    if (Math.abs(u[cx][i] - data.uReal(cx * h1, h2 * i)) > max)
                        max = Math.abs(u[cx][i] - data.uReal(cx * h1, h2 * i));
            }

            if (constVal == 'x')
            {
                for (int i = 0; i < N; i++)
                    if (Math.abs(u[i][cy] - data.uReal(h1 * i, cy * h2)) > max)
                        max = Math.abs(u[i][cy] - data.uReal(cy * h1, h2 * i));
            }
            k++;
        } while (Math.abs(max - oldMax) > eps);
        
        return u;
    }

    public double[][] libman()
    {
        double[][] u = new double[N][N];
        double[][] newU = new double[N][N];

        int n = N;
        double sum = 0;
        for (int j1 = 1; j1 < N - 1; j1++)
            sum = sum+data.phi_1(h2*j1)+ data.phi_2(h2*j1)+data.phi_3(h1*j1)+data.phi_4(h1*j1);

        for (int i = 1; i < N - 1; i++)
            for (int j = 1; j < N - 1; j++)
                u[i][j] = sum/4/(N-1);
        

        for (int j = 0; j < N; j++)
        {
            u[0][j] = (data.phi_1(h2 * j) - (data.Alpha1 / h1) * u[1][j]) / (-data.Alpha1 / h1 + data.Beta1);
            u[N - 1][j] = (data.phi_2(h2 * j) + (data.Alpha2 / h1) * u[N - 2][j]) / (data.Alpha2 / h1 + data.Beta2);
            u[j][0] = (data.phi_3(h1 * j) - (data.Alpha3 / h2) * u[j][1]) / (-data.Alpha3 / h2 + data.Beta3);
            u[j][N - 1] = (data.phi_4(h1 * j) + (data.Alpha4 / h2) * u[j][N - 2]) / (data.Alpha4 / h2 + data.Beta4);
        }
        double max = 0, oldMax;
        int k = 0;
        //for (int iter = 0; iter < eps; iter++ )
        do{
            oldMax = max;
            for (int i = 1; i < N - 1; i++)
                for (int j = 1; j < N - 1; j++)
                {
                    //newU[i][j] = (u[i + 1][j] + u[i - 1][j] + u[i][j + 1] + u[i][j - 1])/4;
                	newU[i][j] = (u[i+1][j]*(1/(h1*h1) - data.A/h1) +
                			u[i-1][j]*(1/(h1*h1)) + u[i][j+1]*(1/(h2*h2)-data.B/h2) + 
                			u[i][j-1]*(1/(h2*h2)))/-(-2/(h1*h1) - 2/(h2*h2) + data.A/h1 + data.B/h2 - data.C);
                }

            for (int j = 0; j < n; j++)
            {
                newU[0][j] = (data.phi_1(h2 * j) - (data.Alpha1 / h1) * newU[1][j]) / (-data.Alpha1 / h1 + data.Beta1);
                newU[n - 1][j] = (data.phi_2(h2 * j) + (data.Alpha2 / h1) * newU[n - 2][j]) / (data.Alpha2 / h1 + data.Beta2);
                newU[j][0] = (data.phi_3(h1 * j) - (data.Alpha3 / h2) * newU[j][1]) / (-data.Alpha3 / h2 + data.Beta3);
                newU[j][n - 1] = (data.phi_4(h1 * j) + (data.Alpha4 / h2) * newU[j][n - 2]) / (data.Alpha4 / h2 + data.Beta4);
            }


            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    u[i][j] = newU[i][j];

            //u[0, 0] = u[0, n - 1] = u[n - 1, 0] = u[n - 1, n - 1] = 1000;

            max = 0;
            if (constVal == 'y')
            {
                for (int i = 0; i < n; i++)
                    if (Math.abs(u[cx][i] - data.uReal(cx * h1, h2 * i)) > max)
                        max = Math.abs(u[cx][i] - data.uReal(cx * h1, h2 * i));
            }

            if (constVal == 'x')
            {
                for (int i = 0; i < n; i++)
                    if (Math.abs(u[i][cy] - data.uReal(h1 * i, cy * h2)) > max)
                        max = Math.abs(u[i][cy] - data.uReal(cy * h1, h2 * i));
            }
            k++;
        } while (Math.abs(max - oldMax) > eps);
        return u;
    }
	
	public double[][] uReal()
	{
		h1 = data.Lx/N;
		h2 = data.Ly/N;
		double U[][] = new double[N+1][M+1];
		
		int i = 0;
		for(double x = data.x0; x <= data.Lx; x += h1)
		{
			if (i == N+1)
				break;
			int j = 0;
			for(double y = data.y0; y <= data.Ly; y += h2)
			{
				if(j == M+1) break;
			
				U[i][j] = data.uReal(x, y);
				++j;
			}
			++i;
		}
		Log.d("uReal in solver size", Integer.toString(U.length));
		return U;
	}
	
}
